package com.company.demo;

import org.junit.Before;
import org.junit.Test;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;


public class Main{


    private List<Fruit> list ;

    @Before
    public void before(){
        list = Fruit.getFruites();
    }

    /**
     * Lambda 表达式可以理解为简洁地表示可传递的匿名函数的一种方式：
     * 它没有名称，但它有参数列表、函数主体、返回类型，可能还有一个可以抛出的异常列表。
     * 使用 Lambda 表达式的代码会更加简洁，可读性更强。
     *
     * Lambda 表达式由三个部分组成:
     * (parameters)  ->   {statements}
     *  形参       操作符    表达式
     * 第一部分为一个圆括号内用逗号分隔的形式参数，参数是函数式接口里面方法的参数
     * 第二部分为一个箭头符号（ Lambda ）：->
     * 第三部分为方法体，可以是表达式和代码块
     */

    @Test
    public  void test_lambda_01(){
        System.out.println("===============java 1.7 =======================");
        //无参匿名函数
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("thread run !");
            }
        };
        runnable.run();
        System.out.println("===============java 1.8 lambda=======================");

        //lambda表达式
        //无参需要定义圆括号
        //主体只包含一个语句 可不需要大括号
        Runnable runnable1 = ()-> {
            System.out.println("thread run 1!");
        };
//        Runnable runnable1 = ()-> System.out.println("thread run 1!");
        runnable1.run();
    }


    @Test
    public void test_lambda_02(){
        System.out.println("===============java 1.7 =======================");
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        System.out.println(comparator.compare(20,10));

        List<Integer> list = Arrays.asList(5,10,15,20,16);
        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        });
        System.out.println(list);

        System.out.println("===============java 1.8 =======================");
        //有参数的匿名类：一个参数以上或者无参 必须加圆括号
        //主体只有一个表达式且有返回值 可不需大括号，译器自动返回值；如果有大括号必须显式指定返回值
        Comparator<Integer> comparator1 = (o1, o2) -> Integer.compare(o1,o2);
        /*Comparator<Integer> comparator1 = (o1, o2) -> {
            return Integer.compare(o1,o2);
        };*/
        System.out.println(comparator1.compare(10,20));
        Collections.sort(list,(o1, o2) -> Integer.compare(o1,o2));
//        Collections.sort(list,(o1, o2) -> {return Integer.compare(o1,o2);});
        System.out.println(list);
    }

    @Test
    public void test_lambda_03(){
        System.out.println("===============java 1.7 =======================");
        Integer result = new MyInterface() {
            @Override
            public Integer add(int n1, int n2) {
                return n1+n2;
            }
        }.add(1,2);
        System.out.println(result);

        System.out.println("===============java 1.8 =======================");
        MyInterface fun = (n1,n2)->n1+n2;
        System.out.println(fun.add(1,2));

    }

    /**
     * Stream 它并不是一个容器，它只是对容器的功能进行了增强，添加了很多便利的操作，例如查找、过滤、分组、排序等一系列的操作。
     * 并且有串行、并行两种执行模式，并行模式充分的利用了多核处理器的优势，使用 fork/join 框架进行了任务拆分，同时提高了执行速度。
     * 简而言之，Stream 就是提供了一种高效且易于使用的处理数据的方式。
     */
    @Test
    public void test_stream_01(){
        List<Integer> list = Arrays.asList(1,3,5,2,6,8);
        System.out.println("===============java 1.7 =======================");
        for (Integer i : list) System.out.println(i);

        //创建stream 流
        System.out.println("===============java 1.8 =======================");
        /*Stream<Integer> stream = list.stream();
        stream.forEach(System.out::println);*/
        list.stream().forEach(System.out::println);
//        list.parallelStream().forEach(System.out::println);

        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
        integerStream.forEach(System.out::println);

        int[] arr = new int[]{1,2,3,4,54,4};
        IntStream stream = Arrays.stream(arr);
        stream.forEach(System.out::println);

        // 迭代
//        Stream<Integer> iterate = Stream.iterate(0, t -> t + 2);
//        iterate.forEach(System.out::println);
        // 生成
        Stream<Double>  generate= Stream.generate(Math::random).limit(15);
        generate.forEach(System.out::println);
    }

    @Test
    public void test_stream_filter(){
        List<Fruit> list = Fruit.getFruites();
//        Predicate<Fruit> predicate = s->s.price>10;
//        Consumer<Fruit> consumer = fruit -> System.out.println(fruit.name);
//        list.stream().filter(predicate).forEach(consumer);

        System.out.println("原价："+list);
        list.stream().filter(fruit->fruit.price<10).forEach(fruit -> {fruit.price+=10;});
        System.out.println("涨价后："+list);
    }

    @Test
    public void test_stream_distinct(){
        List<Fruit> list = Fruit.getFruites();
        list.add(new Fruit(1,"苹果",100,10));
        System.out.println("去重前：");
        list.stream().forEach(System.out::print);
        System.out.println();
        System.out.println("去重后：");
        list.stream().distinct().forEach(System.out::print);
    }


    //接收一个 Function 函数作为参数，该函数会被应用到每个元素上，并将其映射成由新元素组成的 Stream
    @Test
    public void test_stream_map(){
        List<Fruit> list = Fruit.getFruites();
        /*
        list.stream().map(new Function<Fruit, Object>() {
            @Override
            public String apply(Fruit fruit) {
                return fruit.name;
            }
        }).forEach(System.out::println);
         */
        list.stream().map(fruit -> fruit.name).forEach(System.out::println);
    }

    //排序
    @Test
    public void test_stream_sort(){
        List<Integer> nums = Arrays.asList(1,2,3,6,5,4);
        nums.stream().sorted().forEach(System.out::println);
        System.out.println(nums);

        nums.stream().limit(4).forEach(System.out::println);

        List<Fruit> list = Fruit.getFruites();
        list.stream().sorted((o1, o2) -> Double.compare(o1.price,o2.price)).forEach(System.out::println);
    }

    //获取流中的数据状态
    @Test
    public void test_stream_peek(){
        List<Fruit> list = Fruit.getFruites();
        list.stream().peek(s-> System.out.println(s.name)).forEach(System.out::println);
    }

    /**
     * Stream 的终止操作
     * 终止操作将执行中间操作链，并返回结果。在这里列举一下的终止操作：
     *
     * forEach()：循环操作 Stream 数据
     * toArray()：返回流中元素对应的数组对象
     * anyMatch()：检查是否至少匹配一个元素
     * allMatch()：检查是否匹配的所有元素
     * noneMatch()：检查是否没有匹配的元素
     * min()、max()、count()：聚合操作，最小值，最大值，总数量
     * reduce()：聚合操作，用来做统计
     * collect()：聚合操作，封装目标数据
     * findFirst()：获取第一个元素
     * findAny()：获取任一元素
     */

    @Test
    public void test_stream_toArray(){
        Object[] objects = list.stream().map(s -> s.name).toArray();
        for(Object o:objects) System.out.println(o);
        System.out.println("======================");
        Object[] objects1 = list.stream().filter(fruit -> fruit.price > 10).toArray();
        for(Object o:objects1) System.out.println(o);

    }

    @Test
    public void test_stream_match(){
        Stream<Fruit> fruitStream = list.stream();

        boolean anyMatch = fruitStream.anyMatch(fruit -> fruit.price > 10);
        System.out.println(anyMatch);

        boolean allMatch = fruitStream.allMatch(fruit -> fruit.price > 10);
        System.out.println(allMatch);

        boolean noneMatch = fruitStream.noneMatch(fruit -> fruit.price > 10);
        System.out.println(noneMatch);
    }

    @Test
    public void test_stream_min_max_count(){
        System.out.println(list.stream().count());

        Optional<Fruit> max = list.stream().max(((o1, o2) -> Double.compare(o1.price, o2.price)));
        System.out.println(max.get());

        Optional<Fruit> min = list.stream().min((o1, o2) -> Double.compare(o1.price, o2.price));
        System.out.println(min);

    }

    /**
     * reduce 统计
     * reduce(T identity ,BinaryOperator b) 将流中元素反复集合起来，得到一个值，返回 T。reduce 可以传递两个参数，identity 它允许用户提供一个循环计算的初始值。
     */
    @Test
    public void test_stream_reduce(){
        Integer total = list.stream().map(fruit -> fruit.num).reduce(0, Integer::sum);
        System.out.println(total);
    }

    @Test
    public void test_stream_collect(){
        System.out.println("=========collect to list======");
        List<String> names = list.stream().map(fruit -> fruit.name).collect(Collectors.toList());
        names.forEach(System.out::println);
        System.out.println("=========collect to set======");
        Set<String> names1 = list.stream().map(fruit -> fruit.name).collect(Collectors.toSet());
        names1.forEach(System.out::println);
        System.out.println("=========collect to map======");
        Map<Integer, Fruit> fruitMap = list.stream().collect(Collectors.toMap(s -> s.id, Function.identity()));
        fruitMap.forEach((id,fruit)-> System.out.println(id+":"+fruit.name));
    }

}

interface MyInterface{
    Integer add(int n1,int n2);
}

class Fruit{
     int id;

     String name;

     int num;

     double price;

    public Fruit() {
    }
    public Fruit(int id, String name, int num, double price) {
        this.id = id;
        this.name = name;
        this.num = num;
        this.price = price;
    }

    public static List<Fruit> getFruites(){
        List<Fruit> fruits = new ArrayList<>();
        fruits.add(new Fruit(1,"苹果",100,10));
        fruits.add(new Fruit(2,"葡萄",90,19));
        fruits.add(new Fruit(3,"葡萄柚",30,3.9));
        fruits.add(new Fruit(4,"提子",50,6.8));
        fruits.add(new Fruit(5,"构树果实",200,38.9));
        fruits.add(new Fruit(6,"黄心猕猴桃",405,32.8));
        fruits.add(new Fruit(7,"牛油果",45,5.8));
        fruits.add(new Fruit(8,"圣女果",225,12.8));
        fruits.add(new Fruit(9,"白玉樱桃",325,10.5));
        fruits.add(new Fruit(10,"南洋红香蕉",25,15.3));
        return fruits;
    }

    @Override
    public String toString() {
        return this.name + this.price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Fruit fruit = (Fruit) o;
        return id == fruit.id &&
                num == fruit.num &&
                Double.compare(fruit.price, price) == 0 &&
                Objects.equals(name, fruit.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, num, price);
    }
}








